/****************************************************************************

	COPYRIGHT(C) MAXSI SOFTWARE, JONAS 'SORTIE' TERMANSEN 2008, 2009, 2010, 2010

    This file is part of Maxsi Engine.

    Maxsi Engine is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Maxsi Engine is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Maxsi Engine. If not, see <http://www.gnu.org/licenses/>.

	*/ #include "MaxsiEngineContributors.h" /*

	If you modify this file, please enter your name below and provide contact
	information in MaxsiEngineContributors.h. For more information please see
	MaxsiEngineContributors.h.
	
	Contributors to this file:

	- Jonas 'Sortie' Termansen
	- [your name here]

	MaxsiEngine.dll
	A linkable version of Maxsi Engine

	MaxsiWin32.cpp
	Basic Win32 Functionallity

****************************************************************************/

#include "MaxsiEngine.h"
#include "winsock2.h"

namespace MaxsiEngine
{

	/****************************************************************************
		FUNCTIONS
	****************************************************************************/


	ME_Win32Entry*		Entry				=	0;
	bool				IgnoreMEAssertions	=	false;

	bool	SetWin32EntryPointClass	(ME_Win32Entry* NewEntry)
	{
		Entry	=	NewEntry;
		return true;
	}

#ifdef __MINGW_H // MinGW Hack/Workaround
	void* ME_GetExceptionFilter()
	{
		return NULL;
	}
#endif

	bool	IssueMEAssertionFailure(MESTR* Message)
	{
		if ( IgnoreMEAssertions ) { return false; }
		LRESULT		Result		=	ME_SendThreadMessage(Entry->WindowHWND,ME_Assertion_Failure,(LPARAM)Message);
		return (Result != IDABORT);
	}

	
	DWORD	ME_Thread::OnMEAssertionFailure(char* Message)
	{
		int	Result	=	MessageBoxA(
			WindowHWND,
			Message,
			"ME_Thread::OnMEAssertionFailure",
			MB_ICONWARNING | MB_ABORTRETRYIGNORE | MB_DEFBUTTON1 | MB_TASKMODAL | MB_SETFOREGROUND);

		if ( Result == IDABORT )
		{
			//PostQuitMessage(0);
		}
		else if ( Result == IDRETRY )
		{

		}
		else if ( Result == IDIGNORE )
		{
			IgnoreMEAssertions	=	true;
		}
		return (DWORD)(Result);
	}

	HANDLE	ME_CreateThread(LPTHREAD_START_ROUTINE lpStartAddress, HWND Parent)
	{		
		ME_Thread_Parameters* Params	=	new	ME_Thread_Parameters;

		Params->Command					=	ME_Thread_Cmd_GetDefaultStackSpace;

		DWORD	DefaultStackSpace		=	lpStartAddress(Params);		

		Params							=	new	ME_Thread_Parameters;

		Params->Command					=	ME_Thread_Cmd_Create;
		Params->Parent					=	Parent;

		return CreateThread(NULL,DefaultStackSpace,lpStartAddress,Params,0,NULL);		
	}

	
	BOOL ME_PostThreadMessage(HWND hWnd, WPARAM wParam, LPARAM lParam)
	{
		return PostMessage(hWnd,ME_ThreadMessageID,wParam,lParam);
	}

	LRESULT ME_SendThreadMessage(HWND hWnd, WPARAM wParam, LPARAM lParam)
	{
		return SendMessage(hWnd,ME_ThreadMessageID,wParam,lParam);
	}

	UINT	g_ME_ThreadMessageIDFunc	=	0;

	// Returns the ID of the windows message that ME_Thread uses
	const UINT ME_ThreadMessageIDFunc(void)
	{
		if ( g_ME_ThreadMessageIDFunc )
			return g_ME_ThreadMessageIDFunc;

		g_ME_ThreadMessageIDFunc	=	RegisterWindowMessageA("ME_ThreadMessageID");

		return		g_ME_ThreadMessageIDFunc;

		// TODO: Use the command to automatically get a msg that is uniq to ME_Thread
		//RegisterWindowMessage() http://msdn.microsoft.com/en-us/library/ms644947(VS.85).aspx

		// Maybe?
		//return RegisterWindowMessageA("ME_ThreadMessageID");

		//return	0xSORTIE00
		//return		0x50F71300;
		//return		g_ME_ThreadMessageIDFunc;
	}

	
	// Returns the ID of the windows message that ME_Thread uses
	ME_Window* ME_WindowGetPointer(HWND Handle)
	{
		return	(ME_Window*)ME_SendThreadMessage(Handle,ME_Thread_Cmd_GetME_WindowPointer,0);
	}

	/****************************************************************************
		ME_Window::
	****************************************************************************/

	ME_Window::ME_Window()
	{
		WindowHWND		=		0;
		Form			=		0;
		SubWindows		=		0;
	}

	ME_Window::~ME_Window()
	{
		DeleteList(SubWindows);
		// TODO: Delete stuff here!
	}

	BOOL ME_Window::TerminateApplication()
	{
		Form		=		TerminateForm(Form);

		return TRUE;
	}

	BOOL ME_Window::InitApplication(HANDLE hInstance)
	{
		// Should be handled in a child class
		return FALSE;
	}

	BOOL ME_Window::InitInstance(HINSTANCE hInstance, INT nCmdShow)
	{
		// Should be handled in a child class
		return FALSE;
	}	

	LRESULT APIENTRY ME_Window::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
	{
		bool	Handled		=	true;
		LRESULT	Result		=	0;

		if ( message == ME_ThreadMessageID )
		{
			if (wParam == ME_Thread_Cmd_GetME_WindowPointer ){return (LRESULT)this;}
		}

		if ( g_Win7TBC == ULONG_MAX || g_Win7TBC == 0 )
		{
			g_Win7TBC	=	RegisterWindowMessageA("TaskbarButtonCreated");

			if ( g_Win7TBC == 0 )
			{
				ME_UNDEFINED_BEHAVIOR("Failed to register window message TaskbarButtonCreated!");
			}
		}

		if (message==g_Win7TBC)
		{
			EnableWin7TaskBar(hWnd);
		}
		
		switch (message) 
		{
			case WM_COMMAND:
				break;

			//case WM_SIZE:
			//	break;

			//case WM_PAINT:
			//	break;

			case WM_DESTROY:
				PostQuitMessage(0);
				Form	=	TerminateForm(Form);
				break;

			default:
				Handled		=	false;
				
				if (!Form)
				{
					// We don't have a form, simply handle this ourselves.
					Result = DefWindowProc(hWnd, message, wParam, lParam);
				}
		}

		if ( Form )
		{
			if ( Handled )
			{
				// Just forward, but don't return its result
							Form->WindowProc(hWnd,message,wParam,lParam);
			}
			else
			{	
				// We didn't handle it, forward and return the result;
				Result	=	Form->WindowProc(hWnd,message,wParam,lParam);
			}
		}

		return Result;
	}


	/****************************************************************************
		ME_Thread::
	****************************************************************************/

	ME_Thread::ME_Thread()
	{
		hInst			=	GetModuleHandle(NULL);
		SubThreads		=	0;
	}

	ME_Thread::~ME_Thread()
	{
		DeleteList(SubThreads);
		// TODO: Delete stuff here!
	}	

	DWORD ME_Thread::DefaultStackSpace( void )
	{
		return (DWORD)NULL; // NULL means Windows' Default (Which happens to be = 1 MB)
	}

	DWORD WINAPI ME_Thread::EntryPoint(LPVOID parameter)
	{
		ME_Thread_Parameters* Params = (ME_Thread_Parameters*)parameter;

		if ( Params->Command != ME_Thread_Cmd_Create )
		{
			return (DWORD)ME_Thread_Cmd(0,(WPARAM)Params->Command,(LPARAM)Params->Parameter);
		}

		Parent	=	Params->Parent;

		if (InitApplication(hInst) &&
			InitInstance(hInst, Params->nCmdShow))
		{
			if ( !WindowHWND) return 0;

			if ( Parent )
			{
				ME_SendThreadMessage(Parent,ME_Thread_Cmd_Register,(LPARAM)WindowHWND);
			}

			MSG		msg			=	{0};
			BOOL	bRet		=	FALSE;

			// Begin the mainloop
			while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
			{ 
				if (bRet == -1)
				{
					break;
				}
				else
				{
					/* Only translate message if it is not an accelerator message */
					if (!TranslateAccelerator((HWND)WindowHWND, hAccTable, &msg))
					{
						TranslateMessage(&msg);
						DispatchMessage(&msg); 
					}
				}
			}

			return (int) (msg.wParam);
		}
		return 0;
	}

	// Handles internal thread messages, make sure to forward unknown messages to the base class!
	
	LRESULT APIENTRY ME_Thread::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
	{
		UINT	ME_ThreadMessageMSGID	=	ME_ThreadMessageID;
		if ( message == ME_ThreadMessageMSGID )
		{
			return ME_Thread_Cmd(hWnd, wParam,lParam);
		}
			
		// Forward unknown messages to the BaseClass
		return (BaseClass::WindowProc(hWnd, message, wParam, lParam));
	}

	LRESULT ME_Thread::ME_Thread_Cmd(HWND hWnd, WPARAM wParam, LPARAM lParam)
	{
		switch (wParam)
		{
			// Handle Messages here
			case ME_Thread_Cmd_Null: {}	break;
			case ME_Thread_Cmd_Create: {}	break;
			case ME_Thread_Cmd_GetDefaultStackSpace: { return DefaultStackSpace(); }	break;
			case ME_Thread_Cmd_Register: { return OnThreadRegistered((HWND)lParam); }	break;
			case ME_Thread_Cmd_Initialize: { return OnThreadInitialize(lParam); }	break;
			case ME_Thread_Cmd_Terminate: { return OnThreadInitialize(lParam); }	break;
			case ME_Thread_Cmd_SetParent: { return OnNewParent((HWND)lParam); }	break;
			case ME_Assertion_Failure: { return OnMEAssertionFailure((char*)lParam); }	break;

			// Forward unknown messages to the BaseClass
			default:
				return (BaseClass::WindowProc(hWnd, ME_ThreadMessageID, wParam, lParam));
		}
		return (0);
	}

	DWORD ME_Thread::OnThreadRegistered(HWND hWnd)
	{
		SubThreads = AddListEntry(SubThreads,(char*)&hWnd,sizeof(char*));
		return TRUE;
	}
	DWORD ME_Thread::OnNewParent(HWND hWnd)
	{
		Parent = hWnd;
		return TRUE;
	}
	DWORD ME_Thread::OnThreadInitialize(LPARAM lParam)
	{
		// Should be handled in a child class
		return TRUE;
	}
	DWORD ME_Thread::OnThreadTerminate(LPARAM lParam)
	{		
		if (lParam & ME_Thread_Cmd_SelfTerminate || lParam == 0)
		{
			PostQuitMessage(0);
		}
		if (lParam & ME_Thread_Cmd_SelfTerminate || lParam == 0)
		{
			size_t	NumThreads		=	GetListSize(SubThreads);
			for (size_t N = 0; N < NumThreads; N++)
			{
				ME_PostThreadMessage((HWND)GetListEntry(SubThreads,N),ME_Thread_Cmd_Terminate,ME_Thread_Cmd_FullTermination);
			}
		}

		return TRUE;
	}

	/****************************************************************************
		ME_Win32Entry::
	****************************************************************************/

	int WINAPI ME_Win32Entry::WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
	{
		// Initialize the Common Controls
		INITCOMMONCONTROLSEX Controls;

		Controls.dwICC		=	ICC_PROGRESS_CLASS | ICC_WIN95_CLASSES;
		Controls.dwSize		=	sizeof(Controls);

		InitCommonControlsEx(&Controls);

		hInst				=	hInstance;

		ME_Thread_Parameters Params;

		memset(&Params,0,sizeof(Params));

		Params.Command		=	ME_Thread_Cmd_Create;
		Params.nCmdShow		=	nCmdShow;

		return (int) BaseClass::EntryPoint((LPVOID)&Params);
	}

	BOOL ME_Win32Entry::InitApplication(HANDLE hInstance)
	{
		return BaseClass::InitApplication(hInstance);
	}

	BOOL ME_Win32Entry::InitInstance(HINSTANCE hInstance, INT nCmdShow)
	{
		return BaseClass::InitInstance(hInstance,nCmdShow);
	}

	LRESULT APIENTRY ME_Win32Entry::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
	{
		return BaseClass::WindowProc(hWnd,message,wParam,lParam);
	}

}
